/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFVTrack
  \brief class for a track. Note that it is supposed to be a virtual class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-11-01
  \date 2021-12-11 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-11-01   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#ifndef ETFVTrack_h
#define ETFVTrack_h

#include <map>
#include <vector>
#include <string>
#include <TObject.h>
#include "TrackType.h"

class TGraph;
class ETFDCHit;
class ETFDetector;

using std::vector;
using std::string;
using std::map;

/// the following values correspond to track fitting statuses of
enum class FitStatus : short{
  kUNFITTED, ///< not fitted yet
  kGOODFIT, ///< good fit, accepted
  kBADFIT ///< bad fit, rejected
}; ///< FitStatus type

class ETFVTrack : public TObject{
public:
  ETFVTrack();
  ETFVTrack(const ETFVTrack &t); ///< copy ctor
  ETFVTrack &operator=(const ETFVTrack &t); ///< assignment ctor
  virtual ~ETFVTrack();

  virtual void copy(const ETFVTrack *t); ///< the same as copy ctor
  virtual void Initialize();
  virtual void Reset(){ Initialize(); }
  void SetName(const string &n){ fName = n; }
  const char *GetName() const override{ return fName.data(); }
  void SetSlope(double k){ SetK(k); }
  void SetIntercept(double b){ SetB(b); }
  void SetK(double k){ fK = k; }
  void SetB(double b){ fB = b; }
  void SetD2(double d2){ fD2 = d2; }
  void SetDsquare(double d2){ SetD2(d2); }
  void SetChi2(double chi2){ fChi2 = chi2; }
  void SetChiSquare(double chi2){ return SetChi2(chi2); }
  virtual void AddHit(ETFDCHit *h);
  virtual map<int, ETFDCHit *> &GetHits(){ return fHits; }
  virtual void SetHitFiredStatus(int s); ///< set hit fired status for all hits
  virtual const map<int, ETFDCHit *> &GetHits() const{ return fHits; }
  const ETFDCHit *GetHit(int layerId) const{ return (*this)[layerId]; }
  virtual double DistanceToLine(double *p) const{ return DistanceToLine(p[2], p[0]); }
  virtual double DistanceToLine(double z, double x) const;
  virtual void SetDriftTimeStart(double time0); ///< using external time as drift time start
  virtual void UpdateDriftTime(); ///< update drift times for all member hits
  /// number the track, set the name for the track
  virtual void SetId(int id, short type);
  /// fit the track with drift distance info
  virtual bool Fit(); ///< \retval true: if the fitting result meets user criteria
  virtual void UpdateChi(); ///< update the hits' chis using the current (k, b)
  /// test if the DC hits associated with a track candidate is enough, e.g., usually
  /// we require that the 3 DCs all have at least 1 hit, or any two DCs are fired, but
  /// each one should have two layer fired for the track candidate. User can specify
  /// their own criteria by implementing this method in their derived class
  /// \return the number of hits, or 0 if not qualified at all
  virtual int EnoughHit() = 0; ///< specify the criteria that how many hits qualify a good track

  virtual ETFDCHit *operator[](int layerId) const;
  virtual ETFDCHit *at(int layerId) const{ return (*this)[layerId]; }
  virtual int GetNofHits() const{ return fNofHits; }
  virtual double GetChi2() const{ return fChi2; }
  double GetChiSquare() const{ return GetChi2(); }
  double GetChi() const{ return fChi2 / fNofHits; }
  double GetDsquare(){ return GetD2(); }
  double GetD2();
  double GetK() const{ return fK; }
  double GetB() const{ return fB; }
  double GetAng() const; ///< k in angle (in degree): atan(k)/DEGREE
  double GetSlope() const{ return GetK(); }
  double GetIntercept() const{ return GetB(); }
  double GetAngle() const{ return GetAng(); } ///< k in angle: atan(k)/DEGREE
  bool fired(int layerId) const; ///< \return whether layer with layerId is fired
  const vector<int> &GetLayerId() const{ return fLayerId; }

  double k() const{ return fK; }
  double b() const{ return fB; }
  double ang() const{ return GetAngle(); }
  double tot() const{ return fTOTAvrg; }

  FitStatus GetFitStatus() const{ return fFitStatus; }
  double GetDriftTimeStart() const{ return fDT0; }
  virtual void Print(const char *opt = "") const override; ///< print the track info

  virtual void Draw(const char *opt = "") override; ///< draw the track as TGraph
  void SetLineColor(int c){ fColor = c; }
  virtual void ConstructGraph(double z0, double z1); ///< update fGraph in [z0, z1]
  virtual bool GoodFit() const; ///< test if a fit is a good fit (d2, chi and chi2)

  virtual void SetHitPos(const string &d, double x);
  double GetHitPos(const string &d) const;
  double GetHitPos(const char *d) const{ return GetHitPos(string(d)); }
  /// assign hit pos at det's central plane (postMagTrk) or in lab frame (trk around TA)
  virtual void UpdateHitPos(ETFDetector *d);
  virtual void SetChiPD(double chiPD){ fChiPD = chiPD; } // user-input chiPD

  ClassDefOverride(ETFVTrack, 1);

protected:
  string fName; //!
  double fChiPD; //! ///< chi threshold per dot
  map<int, ETFDCHit *> fHits; //! ///< map<layerId, hit>
  vector<int> fLayerId; ///< the layer id of the fired wire
  int fNofHits; ///< number of hits in the track
  double fK, fB; ///< slope and intercept
  double fD2, fChi2; ///< chi-square without (D2) and with drift distances
  double fDT0; ///< drift time start, used for updating drift times for the track's hits
  map<string, double> fHitPosMap; ///< map<det, pos> hit position at each det
  FitStatus fFitStatus; //! ///< fit status
  double fTOTAvrg; ///< the tot averaged over hits

  TGraph *fGraph; //! ///< the graph for the track
  int fColor; //! color of the track for drawing
};

#endif
